/***********************************************************
 *  File_Name  : XJSemaphore.h
 *  File_Path   :
 *  File_Encode : UTF8
 *  Description : 信号量类
 *
 *  OS : Linux, UNIX, Windows
 *  LastVersion  : 20140820
 *
 *  Author      : LiXiujie(李秀杰)
 *  Date        : 2014-8-20
 *  Version     : 20140820
 *  Description :
 *
 *  Edit Author :
 *  Edit Date   :
 *  Version     :
 *  Description :
 *
 ***********************************************************/

#ifndef XJSEMAPHORE_H_
#define XJSEMAPHORE_H_

// #include "XJEnv.h"

#ifndef _WIN32
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <limits.h>
#include <stdint.h>
#ifndef MAXLONG
#define MAXLONG INT_MAX
#endif
//typedef enum _BOOL{FALSE = 0,TRUE = 1}BOOL;
//typedef int LONG;
//typedef uintptr_t DWORD;
//typedef const char *LPCTSTR;

union xj_semun {
    int val;                    /* value for SETVAL */
    struct semid_ds *buf;       /* buffer for IPC_STAT & IPC_SET */
    unsigned short *array;     /* array for GETALL & SETALL */
    struct seminfo *__buf;      /* buffer for IPC_INFO */   //test!!
    void *__pad;
};

#else
#include <windows.h>
#endif


namespace XJUtilClass {

#ifndef _WIN32
class XJSemaphore {
public:
	XJSemaphore(int lMaximumCount, int lInitialCount = 0,
			const char* lpName = NULL, int pSecurityOrID = 0)
	: m_bOK(false)
	{
		m_errno = 0;
		m_key = IPC_PRIVATE;
		if (lpName){
			m_key = ftok(lpName, pSecurityOrID);
			if(m_key == (key_t)-1){
				m_errno = errno;
			}
		}
		if (0 == m_errno){
			m_id = pSecurityOrID;
			m_sem = semget(m_key, 1, IPC_CREAT|IPC_EXCL|0666);
			if(m_sem == -1){
				m_errno = errno;
				if (m_errno == EEXIST){
					m_sem = semget(m_key, 1, 0);
					if(m_sem == -1){
						m_errno = errno;
					}else{
						m_errno = 0;
					}
				}
			}else{
				m_create = true;
			}
		}
		if (m_create && 0 == m_errno){
			union xj_semun arg;
			bool bInit = false;
			/* 当前信号集信息， 内核分配给每个信号集的结构 */
			struct semid_ds semsInfo;
			arg.buf = &semsInfo;
			if (semctl(m_sem, 0, IPC_STAT, arg) == -1){
				m_errno = errno;
			}
			/* sem_otime:最后semop修改时间，创建初始值为0 */
			if (arg.buf->sem_otime == 0){
				bInit = true;
			}
			if (0 == m_errno && bInit){
				/* 第一个创建semaphore进程没有初始化完成或刚好初始化完成，
				 * 本次要初始化一下，否则无法下面的工作。
				 */
				arg.val = lMaximumCount;/* 资源数 */
				if (semctl(m_sem, 0, SETVAL, arg) == -1){
					m_errno = errno;
				}else{
					m_bOK = true;
				}
			}
		}
	}

	~XJSemaphore()
	{
		if (m_create && semctl(m_sem, 0, IPC_RMID, 0) == -1){

		}
	}

	bool Open(const char* lpName, int pSecurityOrID = 0)
	{
		bool bRet = false;
		if (m_create && m_sem != -1 && semctl(m_sem, 0, IPC_RMID, 0) == -1){
			m_errno = errno;
			return bRet;
		}
		m_bOK = false;
		m_errno = 0;
		m_key = IPC_PRIVATE;
		if (lpName){
			m_key = ftok(lpName, pSecurityOrID);
			if(m_key == (key_t)-1){
				m_errno = errno;
				bRet = false;
			}
		}
		if (0 == m_errno){
			m_id = pSecurityOrID;
			m_sem = semget(m_key, 1, 0);
			if(m_sem == -1){
				m_errno = errno;
				bRet = false;
			}else{
				m_errno = 0;
				m_bOK = true;
			}
		}
		return bRet;
	}

	void Wait()
	{
		struct sembuf semBuf;
		semBuf.sem_num = 0;
		semBuf.sem_op = -1;
		semBuf.sem_flg = SEM_UNDO; /* 异常退出资源回收 */
		if (semop(m_sem, &semBuf, 1) != 0){
			m_errno = errno;
		}
	}

	bool Release(short lReleaseCount = 1)
	{
		m_errno = 0;
		struct sembuf semBuf;
		semBuf.sem_num = 0;
		semBuf.sem_op = lReleaseCount;
		semBuf.sem_flg = SEM_UNDO; /* 异常退出资源回收 */
		if (semop(m_sem, &semBuf, 1) != 0){
			m_errno = errno;
			return false;
		}
		return true;
	}
	bool IsValid		()	{return m_bOK;}
private:
	XJSemaphore(const XJSemaphore& sem);
	XJSemaphore operator = (const XJSemaphore& sem);
private:
	int m_key;
	int m_id;
	int m_errno;
	int m_sem;
	bool m_create;
	bool m_bOK;
};

#else

class XJSemaphore {
public:
	XJSemaphore(LONG lMaximumCount, LONG lInitialCount = 0,
			LPCTSTR lpName = nullptr, LONG pSecurityOrID = nullptr)
	: m_bOK(FALSE)
	{
		m_hsem = ::CreateSemaphore((LPSECURITY_ATTRIBUTES)pSecurityOrID, lInitialCount, lMaximumCount, lpName);
		m_bOK = !(IsValid());
	}

	~XJSemaphore()
	{
		if(IsValid())
			::CloseHandle(m_hsem);
	}

	BOOL Open(DWORD dwAccess, BOOL bInheritHandle, LPCTSTR pszName)
	{
		if(IsValid())
			::CloseHandle(m_hsem);
		m_hsem = ::OpenSemaphore(dwAccess, bInheritHandle, pszName);
		return(IsValid());
	}

	void Wait(DWORD dwMilliseconds = INFINITE)
	{
		::WaitForSingleObject(m_hsem, dwMilliseconds);
	}

	BOOL Release(LONG lReleaseCount = 1, LONG * lpPreviousCount = nullptr)
	{
		return ::ReleaseSemaphore(m_hsem, lReleaseCount, lpPreviousCount);
	}

	HANDLE& GetHandle	() 	{return m_hsem;}
	operator HANDLE		()	{return m_hsem;}
	BOOL IsValid		()	{return m_hsem != nullptr;}
private:
	XJSemaphore(const XJSemaphore& sem);
	XJSemaphore operator = (const XJSemaphore& sem);
private:
	HANDLE m_hsem;
	BOOL m_bOK;
};
#endif
} /* namespace XJUtilClass */

#endif /* XJSEMAPHORE_H_ */
